/*
 *	CHANGE LOG
 * 
 * 01.00.00
 * 
 *		10/25/2008 - DW - Created initial version of the provider
 * 
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Text.RegularExpressions;
using System.Web;
using DotNetNuke.Common;
using DotNetNuke.Common.Utilities;
using DotNetNuke.Entities.Host;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Entities.Portals;
using DotNetNuke.Entities.Users;
using Engage.Dnn.Publish;
using DataProvider=DotNetNuke.Data.DataProvider;

namespace ITCrossing
{
	public class metaPostEngageNewsArticleProvider : IPublishable 
	{
		private bool _setAsEndDate = false;

		# region IPublishable Members

		# region Public Properties
		
		/// <summary>
		/// ProviderKey should be set to the Friendly Name of the module for
		/// which this provider is being implemented.
		/// </summary>
		public string ProviderKey
		{
			get
			{
				return MetaPostServices.GetFriendlyNameFromModuleDefinition("Engage: Publish");
			}
		}
		
		/// <summary>
		/// ManifestPath is the path to the manifest file used to determine 
		/// the settings available in Windows Live Writer.  An example path might
		/// be /DesktopModules/itcMetaPost/manifests/wlwblog.xml.  See the following URL for 
		/// information regarding what Content can be placed in a WLW manifest file:
		/// http://msdn2.microsoft.com/en-us/library/bb463260.aspx
		/// </summary>
		public string ManifestFilePath
		{
			get { return "/DesktopModules/itcMetaPost/manifests/wlwEngagePublish.xml"; }
		}
		
		/// <summary>
		/// LocalizationFilePath is the path including the filename to the 
		/// resource file used by metaPost to retrieve the error messages
		/// shown to the user (eg. "/DesktopModules/itcMetaPost/App_LocalResources/itcMetaPost")
		/// </summary>
		public string LocalizationFilePath
		{
			get { return "/DesktopModules/itcMetaPost/App_LocalResources/itcMetaPostEngagePublish"; }
		}
		
		/// <summary>
		/// OPTIONAL - ImageUploadPath is a property metaPost uses to determine where 
		/// to store the media files (images).  This property should return just a 
		/// section of the path starting from the portal root.  Added to this path will
		/// be the username along with the path 
		/// </summary>
		public string ImageUploadPath
		{
			get
			{
				// This can be overridden with custom implementation if needed.  If
				// overridden, then a message should be placed in the ImagePath column
				// for your provider letting administrators know that administration of
				// the ImagePath is implemented elsewhere.  
				return MetaPostServices.GetProviderImageUploadPath(ProviderKey);
			}
		}
		
		/// <summary>
		/// AttachmentUploadPath is used to specify the location of attachments added
		/// to a post or page.
		/// </summary>
		public string AttachmentUploadPath
		{
			get
			{
				// For now, we'll just set this to the same as the image upload path
				return MetaPostServices.GetProviderAttachmentUploadPath(ProviderKey);
			}
		}

		/// <summary>
		/// SettingsUserControlPath is for future use.  It will return the path to the
		/// the settings user control for this provider.  
		/// </summary>
		public string SettingsUserControlPath
		{
			get { throw new NotImplementedException(); }
		}

		# endregion

		# region Get Methods

		/// <summary>
		/// The purpose of this procedure is to provide a list of all the modules in a 
		/// given portal to which the currently logged on user has either insert, update
		/// or delete access.  Note, this may not always correspond to an actual module.
		/// For example, with the DNN Blog module provider, a list of blogs is returned
		/// along with URL of where each blog is available within the site.  For the News
		/// Articles module, we return a list of all News Articles modules that have been
		/// added to Tabs throughout the site.
		/// </summary>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <returns></returns>
		/// <param name="providerKey"></param>
		public ModuleInfoStruct[] GetModulesForUser(UserInfo userInfo, PortalSettings portalSettings,
													string providerKey)
		{

			ModuleInfoStruct[] infoArray = null;
			List<ModuleInfoStruct> moduleInfoList = new List<ModuleInfoStruct>();

			List<DnnModuleInfoStruct> dnnModuleInfos = MetaPostServices.GetModuleInfos(providerKey, portalSettings.PortalId);

			if (dnnModuleInfos.Count > 0)
			{
				infoArray = new ModuleInfoStruct[dnnModuleInfos.Count];
				for (int i = 0; i < dnnModuleInfos.Count; i++)
				{
					// Check to see if the user has the right level of permission to post to this 
					// module.
					if (IsUserPrivileged(userInfo, "PublishAuthorRole0", dnnModuleInfos[i].ModuleId))
					{
						// Yes!  We have a role match, so user has auth priviledge
						// for this particular Engage: Publish module.
						ModuleInfoStruct moduleInfo = new ModuleInfoStruct();
						ModuleController moduleController = new ModuleController();
						ModuleInfo dnnModuleInfo =
							moduleController.GetModule(dnnModuleInfos[i].ModuleId, dnnModuleInfos[i].TabId, false);
						moduleInfo.ModuleLevelId = dnnModuleInfos[i].TabModuleId.ToString();
						string modulePath = dnnModuleInfos[i].TabPath.Replace("//", "/");
						// remove the beginning forward slash
						if (modulePath.Length > 1 && modulePath.Substring(0, 1) == "/")
							modulePath = modulePath.Substring(1);
						moduleInfo.ModuleName = dnnModuleInfo.ModuleTitle + " (" + modulePath + ")";

						moduleInfo.Url = MetaPostServices.GetRedirectUrl(providerKey, dnnModuleInfos[i].TabId);
						moduleInfoList.Add(moduleInfo);
						//break;
					}
				}
				infoArray = moduleInfoList.ToArray();
			}
			else
			{
				throw new MetaPostException("NoModulesForUser",
											"There are no News Article modules associated with this user account.  Please enter a user account which has been associated with a blog.");
			}
			return infoArray;
		}
		/// <summary>
		/// GetRecentItems returns a list of the recent items added to the module.  For example, with
		/// the blog module, the list would return all the recent blog entries.  For the Text/HTML provider, 
		/// this method would return a list of the recently added Text/HTML modules in the site.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="numberOfItems"></param>
		/// <param name="requestType">GetRecentItems can be called for a number of reasons.  In Windows Live Writer,
		/// users are able to retrieve a list of recent posts or recent pages.  The requestType parameter is used
		/// to denote whether a list of pages or posts is being requested.  Additionally, WLW may request a list 
		/// of hiearchical pages to show pages when allowing a parent page to be assigned to a new page.  The values for this enum
		/// are RecentPosts, RecentPages and RecentPagesHiearchical.</param>
		/// <param name="providerKey"></param>
		/// <returns></returns>
		public Item[] GetRecentItems(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings, int numberOfItems, RecentItemsRequestType requestType, string providerKey)
		{
			List<Item> itemArray = new List<Item>();
			int moduleId = Convert.ToInt32(moduleLevelId);
			// Get the actual ModuleId since we're tracking the TabModuleId in the moduleLevelId parameter
			DnnModuleInfoStruct moduleInfo = MetaPostServices.GetModuleInfo(moduleId);
			int articleModuleId = moduleInfo.ModuleId;

			ArrayList articleList = new ArrayList();

			//For admins and superuser accounts, we let them see all the entries except the one which have been 
			// submitted as draft.
			int userId = userInfo.UserID;
			if (userInfo.IsSuperUser || IsAdmin(userInfo.Roles))
			{
				userId = Null.NullInteger;
			}

			string addWhere = string.Empty;
			if (!IsUserPrivileged(userInfo, "PublishAdminRole0", moduleInfo.ModuleId))
			{
				// User is not privileged, so we'll only show the drafts
				addWhere = " AND ApprovalStatusId <= 3 ";
			}
			string SQL = "SELECT Name, ItemId, ArticleText, CreatedDate, DisplayTabID, Disabled, Description, MetaKeywords, MetaDescription, MetaTitle, AuthorUserId, LastUpdated, ModuleId, ItemIdentifier, ItemIdentifier "
			+ "FROM "
			+ MetaPostServices.DatabaseOwner + MetaPostServices.ObjectQualifier
			+ "Publish_vwArticles "
			+ "WHERE PortalID = "
			+ portalSettings.PortalId.ToString()
			+ " AND IsCurrentVersion = 1 AND Disabled = 0 " + addWhere
			+ "ORDER BY [Name]";


            using (IDataReader dr = DataProvider.Instance().ExecuteSQL(SQL))
            {

                int i = 0;
                while (dr.Read())
                {
                    Item item = new Item();
                    item.Link = string.Empty;
                    item.Content = HttpUtility.HtmlDecode(dr["ArticleText"].ToString() ?? " ");
                    item.DateCreated = (DateTime)dr["CreatedDate"];

                    item.Title = dr["Name"].ToString();
                    item.Permalink = string.Empty;

                    item.ItemId = dr["ItemId"].ToString();
                    itemArray.Add(item);
                    if (i >= numberOfItems) break;
                    i++;
                }
            }

			return itemArray.ToArray();
		}

		/// <summary>
		/// GetItem is used to retrieve a specific item, which may be a page or a post if your module supports
		/// both concepts.  In cases where a module supports both pages and posts, a concept made available in 
		/// Windows Live Writer, but not in Word 2007 or in many other publishing clients, the itemType parameter 
		/// can be used to distinguish between when this method is being called to add a page or a post.  If your 
		/// provider doesn't support a distinction between pages and posts, then simply ignore the itemType parameter.
		/// </summary>
		/// <param name="itemId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="itemType"></param>
		/// <returns></returns>
		public Item GetItem(string itemId, UserInfo userInfo, PortalSettings portalSettings, ItemType itemType)
		{
			Item item = new Item();
			int pageId = -1;
			int articleId = -1;
			string imageUrl = string.Empty;

			Article av = Article.GetArticle(Convert.ToInt32(itemId));

			if (av == null)
			{
				throw new MetaPostException("NoPostAvailable",
											"There was en error retrieving the blog entry.  Try closing and restarting the software you're using to edit your blog post.");
			}

			//item.Link = articleInfo.Url;

			item.ItemId = av.ItemId.ToString();
			item.Title = av.Name;
			item.DateCreated = Convert.ToDateTime(av.CreatedDate);
			item.Permalink = Globals.NavigateURL(av.DisplayTabId);//string.Empty;
			item.StartDate = Convert.ToDateTime(av.StartDate);
			item.ExtendedContent = HttpUtility.HtmlDecode(av.ArticleText);
			imageUrl = av.Thumbnail;

			item.Content = HttpUtility.HtmlDecode(av.Description);
			item.Keywords = av.MetaKeywords;

			// Get the Categories for the article

			List<ItemCategory> articleCategories = GetArticleCategories(av);
			List<string> catValues = new List<string>();

			foreach (ItemCategory cat in articleCategories)
			{
				catValues.Add(cat.CategoryName);
			}

			// Check to see if any of the settings categories need to be added:
			// Left for referenece in case a feature like this would be useful for the Engage: Publish provider
			//if (articleInfo.IsFeatured)
			//{
			//    catValues.Add(MetaPostServices.GetString("Featured", "Featured", LocalizationFilePath));
			//}
			//if (articleInfo.IsSecure)
			//{
			//    catValues.Add(MetaPostServices.GetString("Secure", "Secure", LocalizationFilePath));
			//}

			item.Categories = catValues.ToArray();

			ShowFeaturedImage(ref item, imageUrl);

			return item;
		}

		# endregion

		# region Add, Edit and Delete Methods

		/// <summary>
		/// NewItem is used for creating new items (either pages or posts).  The implementation 
		/// in your provider can distinguish between page and post requests for NewItem by the 
		/// ItemType property of the Item struct.  Additional properties of the Item Struct which 
		/// may be of interest in the implmentation of this method are: Publish, StyleDetectionPost,StyleId, and ItemType.
		/// See the comments associated with each of these properties of the Item struct for more information.
		/// </summary>
		/// <param name="moduleLevelId">String - ModuleId is a very loose concept here.  Anything can be tracked in this variable as long as it is consistent.  For the Blog module, the BlogId is passed and for the Text/HTML module, the ModuleId is passed and for the Engage News Articles module, the ArticleId is passed.  This value is based on the value sent to Windows Live Writer when GetModulesForUser is called. </param>
		/// <param name="userInfo">DotNetNuke UserInfo object.  This UserInfo object is retrieved by metaPost and made available to every method call in the MetaPostProvider interface.</param>
		/// <param name="portalSettings">DotNetNuke PortaSettings object - This is provided to make it easier to work with metaPost.  metaPost does all the work behind the scenes of identifying the right portalSettings object based on the requested URL.</param>
		/// <param name="item">Custom Struct - The item struct contains a list of fields sent through the MetaWeblog API.  The properties have been renamed to make sense in more general applications outside of just blogs.</param>
		/// <returns></returns>
		public string NewItem(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings, Item item)
		{
			List<string> images = MetaPostServices.GetImageListFromItem(ref item);
			int itemId;
			string featuredImage;
			
			//ExtractSummaryFromExtendedContent(ref item);

			// Now that we know the content is either in item.Summary or item.Content, we 
			// try to extract the image from the content
			featuredImage = GetFeaturedImage(ref item);

			// Get the actual ModuleId since we're tracking the TabModuleId in the moduleLevelId parameter
			DnnModuleInfoStruct moduleInfo = MetaPostServices.GetModuleInfo(Convert.ToInt32(moduleLevelId));

			item.ItemId = -1;

			itemId = UpdateArticle(item, moduleInfo, featuredImage, userInfo, portalSettings);

			MetaPostServices.SaveItemImages(itemId, images, ProviderKey, portalSettings, userInfo.UserID);
				
			return itemId.ToString(); 
		}

		/// <summary>
		/// EditItem is called to edit either a page or a post.  The difference
		/// between the two is denoted in a property of the Item struct (ItemType).
		/// An additional property of the Item struct that may be important to the 
		/// implementation of the EditItem method is the Publish property.  This is 
		/// set to true when the item should be published.  The default setting of 
		/// false denotes that the item may be saved as a draft.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="item"></param>
		/// <returns></returns>
		public bool EditItem(string moduleLevelId, UserInfo userInfo, PortalSettings portalSettings, Item item)
		{
			List<string> images = MetaPostServices.GetImageListFromItem(ref item);
			string featuredImage;

			//ExtractSummaryFromExtendedContent(ref item);

			// Now that we know the content is either in item.Summary or item.Content, we 
			// try to extract the image from the content
			featuredImage = GetFeaturedImage(ref item);

			// Get the actual ModuleId since we're tracking the TabModuleId in the moduleLevelId parameter
			DnnModuleInfoStruct moduleInfo = MetaPostServices.GetModuleInfo(Convert.ToInt32(moduleLevelId));

			UpdateArticle(item, moduleInfo, featuredImage, userInfo, portalSettings);

			MetaPostServices.SaveItemImages(Convert.ToInt32(item.ItemId), images, ProviderKey, portalSettings, userInfo.UserID);

			return true;
		}

		/// <summary>
		/// DeleteItem is used to delete either a page or a post.  The difference between the two is noted 
		/// in the itemType parameter which is an enumeration set to either Page or Post.
		/// </summary>
		/// <param name="itemId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <param name="itemType"></param>
		/// <returns></returns>
		public bool DeleteItem(string itemId, UserInfo userInfo, PortalSettings portalSettings, ItemType itemType)
		{
			int id;

			Article.DeleteItem(Convert.ToInt32(itemId));

			return true;
		}

		# endregion

		# region Category Related Procedures

		/// <summary>
		/// GetCategories retrieves a list of categories for a particular module.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <returns></returns>		
		public ItemCategoryInfo[] GetCategories(string moduleLevelId, UserInfo userInfo,
												PortalSettings portalSettings)
		{
			List<ItemCategoryInfo> categories = new List<ItemCategoryInfo>();

			// Get the actual ModuleId since we're tracking the TabModuleId in the moduleLevelId parameter
			DnnModuleInfoStruct moduleInfo = MetaPostServices.GetModuleInfo(Convert.ToInt32(moduleLevelId));
			int articleModuleId = moduleInfo.ModuleId;

			List<ItemCategoryInfo> categoryList = GetModuleCategories(portalSettings.PortalId);

			//NOTE: These are included to show how to use the categories section to also allow the user to 
			// configure settings related to your module.  The settings included below are for the News Articles
			// module, but settings for Engage: Publish will be different.  Leave the first entry, which includes 
			// a Settings node in the categories.  Add new settings entries as needed by ensuring that a distinct 
			// CategoryId is used and that the ParentId is set to 2000001 (the CategoryId for the Settings node).

			// Add the Settings Categories
			ItemCategoryInfo settingsCategory = new ItemCategoryInfo();
			settingsCategory.CategoryId = 2000001;
			// Note that if the value in the second argument changes, please update the GetCategoriesToExclude
			// and ProcessExcludeCategories procedures below.
			settingsCategory.CategoryName = MetaPostServices.GetString("PublishSettings", "Settings", LocalizationFilePath, portalSettings.DefaultLanguage);
			settingsCategory.Description = settingsCategory.CategoryName;
			settingsCategory.HtmlUrl = "http://www.engagemodules.com/" + settingsCategory.CategoryId.ToString();
			settingsCategory.RssUrl = "http://www.engagemodules.com/" + settingsCategory.CategoryId.ToString();
			categories.Add(settingsCategory);

			settingsCategory = new ItemCategoryInfo();
			settingsCategory.CategoryId = 2000005;
			// Note that if the value in the second argument changes, please update the GetCategoriesToExclude
			// and ProcessExcludeCategories procedures below.
			settingsCategory.CategoryName = MetaPostServices.GetString("DateAsExpiration", "Date as Expiration", LocalizationFilePath, portalSettings.DefaultLanguage);
			settingsCategory.Description = settingsCategory.CategoryName;
			settingsCategory.HtmlUrl = "http://www.engagemodules.com/" + settingsCategory.CategoryId.ToString();
			settingsCategory.RssUrl = "http://www.engagemodules.com/" + settingsCategory.CategoryId.ToString();
			settingsCategory.ParentId = 2000001;
			categories.Add(settingsCategory);


			if (categoryList.Count > 0)
			{
				// Add the Categories Category
				ItemCategoryInfo categoriesCategory = new ItemCategoryInfo();
				categoriesCategory.CategoryId = 2000004;
				// Note that if the value in the second argument changes, please update the GetCategoriesToExclude
				// and ProcessExcludeCategories procedures below.
				categoriesCategory.CategoryName =
					MetaPostServices.GetString("PublishCategories", "Categories",
											   LocalizationFilePath, portalSettings.DefaultLanguage);
				categoriesCategory.Description = categoriesCategory.CategoryName;
				categoriesCategory.HtmlUrl = "http://www.engagemodules.com/" + settingsCategory.CategoryId.ToString();
				categoriesCategory.RssUrl = "http://www.engagemodules.com/" + settingsCategory.CategoryId.ToString();
				categories.Add(categoriesCategory);

				foreach (ItemCategoryInfo categoryInfo in categoryList)
				{
					ItemCategoryInfo category = new ItemCategoryInfo();

					//First Category
					category.CategoryId = categoryInfo.CategoryId;
					category.Description = categoryInfo.CategoryName;
					category.CategoryName = categoryInfo.CategoryName;
					category.HtmlUrl = "http://www.engagemodules.com/" + settingsCategory.CategoryId.ToString();
					category.RssUrl = "http://www.engagemodules.com/" + settingsCategory.CategoryId.ToString();
					category.ParentId = (categoryInfo.ParentId != 5) ? categoryInfo.ParentId : 2000004;
					categories.Add(category);
				}
			}

			return categories.ToArray();
		}

		public bool SetItemCategories(string itemId, ItemCategory[] itemCategories, UserInfo userInfo,
		                        PortalSettings portalSettings)
		{


			// Retrieve the Article
			Article av = Article.GetArticle(Convert.ToInt32(itemId));
			
			// next, we load a list of categories that should be excluded (Settings
			// and categorization categories.  
			List<string> categoriesToExclude = GetCategoriesToExclude(portalSettings);

			List<ItemCategory> currentCategories = GetArticleCategories(av);
			List<ItemCategory> catToAdd = new List<ItemCategory>();
			List<ItemCategory> catToDelete = new List<ItemCategory>();
			int tutorialId = Convert.ToInt32(itemId);

			// See which categories are not in the list
			foreach (ItemCategory existingCat in currentCategories) // Loop through the existing categories
			{
				bool catIsInList = false;
				foreach (ItemCategory ic in itemCategories) // Loop through the new categories
				{
					if (ic.CategoryId == existingCat.CategoryId)
					{
						catIsInList = true;
						break;
					}
				}
				if (!catIsInList)
				{
					catToDelete.Add(existingCat);
				}
			}

			// See which categories are in the list
			foreach (ItemCategory ic in itemCategories) // Loop through new categories
			{
				bool catIsInList = false;
				foreach (ItemCategory existingCat in currentCategories) // Loop through existing categories
				{
					if (ic.CategoryId == existingCat.CategoryId)
					{
						catIsInList = true;
						break;
					}
				}
				if (!catIsInList)
				{
					catToAdd.Add(ic);
				}
			}

			// Add new categories
			int i = 0;
			foreach (ItemCategory itemCategory in catToAdd)
			{
				// Get the maximum view order
				// Note - the IsPrimary feature doesn't seem to work within WLW, 
				// so we'll just add the first category as the primary and all other categories
				// as related.
				//int relType = (itemCategory.IsPrimary) ? 28 : 29;

				// For first category, we add as both primary and related
				if (i == 0) ItemRelationship.AddItemRelationship(av.ItemId, av.ItemVersionId, Convert.ToInt32(itemCategory.CategoryId), 28, DateTime.Now.ToString(), string.Empty, 0);
				else ItemRelationship.AddItemRelationship(av.ItemId, av.ItemVersionId, Convert.ToInt32(itemCategory.CategoryId), 29, DateTime.Now.ToString(), string.Empty, 0);
				i++;
			}

			// Remove old categories no longer needed
			foreach (ItemCategory itemCategory in catToDelete)
			{
				// Get the maximum view order
				int relType = (itemCategory.IsPrimary) ? 28 : 29;
				// How do you delete an item relationship
				// ItemRelationship.DeleteItemRelationship(av.ItemId, av.ItemVersionId, Convert.ToInt32(itemCategory.CategoryId), relType, DateTime.Now.ToString(), string.Empty, 0);
			}

			return true;
		}

		public ItemCategory[] GetItemCategories(string itemId, UserInfo userInfo,
		                                        PortalSettings portalSettings)
		{
			Article av = Article.GetArticle(Convert.ToInt32(itemId));
			List<ItemCategory> itemCategories = GetArticleCategories(av);

			

			// Check to see if any of the settings categories need to be added:
			// NOTE: The following is left for reference to show how the settings related categories
			// were handled in the News Articles procedure.
			//if (articleInfo.IsFeatured)
			//{
			//    ItemCategory ic = new ItemCategory();
			//    ic.CategoryId = "2000002";
			//    ic.CategoryName = MetaPostServices.GetString("Featured", "Featured", LocalizationFilePath);
			//    itemCategories.Add(ic);
			//}
			//if (articleInfo.IsSecure)
			//{
			//    ItemCategory ic = new ItemCategory();
			//    ic.CategoryId = "2000003";
			//    ic.CategoryName = MetaPostServices.GetString("Secure", "Secure", LocalizationFilePath);
			//    itemCategories.Add(ic);
			//}

			return itemCategories.ToArray();
		}

		/// <summary>
		/// Used to add a new category for modules which support this feature.
		/// </summary>
		/// <param name="moduleLevelId"></param>
		/// <param name="userInfo"></param>
		/// <param name="portalSettings"></param>
		/// <returns></returns>
		public int NewCategory(string moduleLevelId, NewItemCategory category, UserInfo userInfo, PortalSettings portalSettings)
		{

			throw new MetaPostException("FeatureNotImplemented", "This feature is currently not implemented.");
		}

		# endregion

		#endregion

		# region Private Procedures Specific to Engage News Articles Module

		private void ExtractSummaryFromExtendedContent(ref Item item)
		{
			if (!string.IsNullOrEmpty(item.ExtendedContent))
			{
				// Extract the summary from the excerpt
				item.Summary = item.Content;
				item.Content = item.ExtendedContent;
			}
		}

		private void FindAndPlaceSummary (ref Item item)
		{
			if (!string.IsNullOrEmpty(item.Summary) 
				&& !string.IsNullOrEmpty(item.Content)) //MetaPostServices.ContainsHtml(item.Summary)
			{
				// We have HTML, so put the HTML in the content above a <!--more--> tag
				item.Content = item.Summary + "<!--more-->" + item.Content;
				item.Summary = "";
			}
			else if (!string.IsNullOrEmpty(item.Summary) && string.IsNullOrEmpty(item.Content))
			{
				item.Content = item.Summary;
				item.Summary = string.Empty;
			}

		}

		private List<string> GetCategoriesToExclude(PortalSettings portalSettings)
		{
			List<string> categoriesToExclude = new List<string>();
			//TODO - These are the categories that we will ignore.  We need this since we're using the categories
			// for more than just tracking article categories.  The Date as Expiration setting was included to show
			// how this can be used to add article related categories settings that require only a boolean value.

			categoriesToExclude.Add(MetaPostServices.GetString("PublishSettings", "Settings", LocalizationFilePath, portalSettings.DefaultLanguage));
			categoriesToExclude.Add(MetaPostServices.GetString("DateAsExpiration", "Date as Expiration", LocalizationFilePath, portalSettings.DefaultLanguage));
			categoriesToExclude.Add(MetaPostServices.GetString("PublishCategories", "Categories", LocalizationFilePath, portalSettings.DefaultLanguage));

			return categoriesToExclude;
		}

		private void ProcessSettingsFromCategories(string[] categories, PortalSettings portalSettings)
		{

			foreach (string category in categories)
			{
				if (category == MetaPostServices.GetString("DateAsExpiration", "Date as Expiration", LocalizationFilePath, portalSettings.DefaultLanguage))
				{
					_setAsEndDate = true;
				}
			}

		}

		private bool IsUserPrivileged(UserInfo userInfo, string roleGroup, int moduleLevelId)
		{
			if (userInfo.IsSuperUser)
			{
				return true;
			}
			if (IsAdmin(userInfo.Roles))
			{
				return true;
			}
			string hostSetting = HostSettings.GetHostSetting(roleGroup);  //PortalSettings.GetModuleSettings(moduleLevelId);
			if (!string.IsNullOrEmpty(hostSetting))
			{
                string roles = hostSetting;
				string[] rolesArray = roles.Split(';');
				foreach (string role in rolesArray)
				{
					if (userInfo.IsInRole(role))
					{
						return true;
					}
				}
			}
			return false;
		}

		private bool IsAdmin(string[] roles)
		{
			foreach (string role in roles)
			{
				if (role == "Administrators" || role.ToLower() == "mp_na_editor") return true; 
			}
			return false;
		}

		private string GetFeaturedImage(ref Item item)
		{

			// Regex for stripping off the anchor tag for ai images.
			// this new approach returns the href of the anchor tag surrounding the image tag.
			string regex = @"<a[^>]+?(?:png|jpg|jpeg|gif)[^>]+?>[^<]*?<img[^>]+?alt=""ai""[^>]+>[^<]*?</a>";
			// Old approach (left for reference) used the src of the image tag
			//string regex = "<img[^>]+?alt=\"ai\".*?>";
			RegexOptions options = RegexOptions.IgnoreCase | RegexOptions.Singleline;

			string imageUrl = GetFeaturedImageUrl(item.Summary, regex);
			if (!string.IsNullOrEmpty(imageUrl))
			{
				// Remove the image from the Summary
				item.Summary = Regex.Replace(item.Summary, regex, string.Empty, options);
				return imageUrl;
			}

			// Now, it's possible that the image was actually in the content.  So, we'll 
			// check there if the imageUrl is empty.
			imageUrl = GetFeaturedImageUrl(item.Content, regex);
			if (!string.IsNullOrEmpty(imageUrl))
			{
				// Remove the image from the Content
				item.Content = Regex.Replace(item.Content, regex, string.Empty, options);
				return imageUrl;
			}

			// Finally, it's also possible that the image was placed in the extended 
			// content.  We'll check here if the imageUrl is still empty.
			imageUrl = GetFeaturedImageUrl(item.ExtendedContent, regex);
			if (!string.IsNullOrEmpty(imageUrl))
			{
				// Remove the image from the Content
				item.ExtendedContent = Regex.Replace(item.ExtendedContent, regex, string.Empty, options);
				return imageUrl;
			}

			return imageUrl;

		}

		private string GetFeaturedImageUrl(string input, string regex)
		{
			string imageUrl = string.Empty;

			if (!string.IsNullOrEmpty(input))
			{
				RegexOptions options = RegexOptions.Singleline | RegexOptions.IgnoreCase;

				MatchCollection matches = Regex.Matches(input, regex, options);
				foreach (Match match in matches)
				{
					// extract the src attribute from the image
					// actually, now we've changed this to extract the href of the 
					// anchor that surrounds the image.
					string regexSrc = "href=\"(?<src>[^\"]+?)\"";
					input = match.Value;
					Match src = Regex.Match(input, regexSrc, options);
					if (src != null && src.Groups["src"].Captures.Count > 0)
					{
						// We have an image Url
						imageUrl = src.Groups["src"].Captures[0].Value;
						break;
					}
				}
			}

			return imageUrl;
		}

		private void ShowFeaturedImage(ref Item item, string imageUrl)
		{
			if (!string.IsNullOrEmpty(imageUrl))
			{
				string imageTag = "<img src=\"" + imageUrl + "\" alt=\"ai\" />";
				item.Content = imageTag + item.Content;
			}
		}

		private List<ItemCategory> GetArticleCategories (Article av)
		{
			List<ItemCategory> categories = new List<ItemCategory>();

			List<string> catValues = new List<string>();
			// Parent Categories
			ArrayList alItemRelationships = ItemRelationship.GetItemRelationships(av.ItemId, av.ItemVersionId, 28, false);
			foreach (ItemRelationship ir in alItemRelationships)
			{
				string parentName = Engage.Dnn.Publish.Util.ItemType.GetItemName(ir.ParentItemId);

				string catName = parentName; //ir.ParentItemId + "-" + parentName;

				ItemCategory ic = new ItemCategory();
				ic.CategoryId = ir.ParentItemId.ToString();
				ic.CategoryName = parentName;
				ic.IsPrimary = true;

				categories.Add(ic);
			}
			// Related Categories
			alItemRelationships = ItemRelationship.GetItemRelationships(av.ItemId, av.ItemVersionId, 29, false);
			foreach (ItemRelationship ir in alItemRelationships)
			{
				string parentName = Engage.Dnn.Publish.Util.ItemType.GetItemName(ir.ParentItemId);

				string catName = parentName; //ir.ParentItemId + "-" + parentName;

				ItemCategory ic = new ItemCategory();
				ic.CategoryId = ir.ParentItemId.ToString();
				ic.CategoryName = parentName;
				ic.IsPrimary = false;

				// Check first to make sure we aren't adding a duplicate
				if (!categories.Contains(ic)) categories.Add(ic);
			}

			return categories;
		}
		private int UpdateArticle(Item item, DnnModuleInfoStruct moduleInfo, string featuredImage, UserInfo userInfo, PortalSettings portalSettings)
		{
			Article av;
			int articleId;

			// Check first to make sure we have categories selected
			if (item.Categories.Length == 0)
			{
				throw new MetaPostException("NoCategorySelected", "Please select a category.");				
			}

			if (Convert.ToInt32(item.ItemId) == -1)
			{
				av = Article.Create(portalSettings.PortalId);
				
				// Set the DisplayTabId for this new article
				av.DisplayTabId = moduleInfo.TabId;
			}
			else
			{
				av = Article.GetArticle(Convert.ToInt32(item.ItemId));
			}

			// Get Settings passed in through Categories
			ProcessSettingsFromCategories(item.Categories, portalSettings);

			av.Name = item.Title;
			//TODO - Come back and add code to check to see if extendedcontent is entered.
			// if not, then copy the content from Content into ExtendedContent.  This could be
			// done in a revised version of ExtractSummaryFromExtendedContent
			av.ArticleText = item.ExtendedContent;//HttpUtility.HtmlEncode(item.ExtendedContent);
			if (_setAsEndDate && item.DateCreated > DateTime.Now.AddDays(-1))
			{
				av.EndDate = item.DateCreated.ToString();
				av.CreatedDate = DateTime.Now.ToString(); 
			}
			else
			{
				av.CreatedDate = DateTime.Now.ToString();				
			}
			av.MetaDescription = item.Summary;//HttpUtility.HtmlEncode(item.Summary);
			av.Description = item.Content;//HttpUtility.HtmlEncode(item.Content);

			// Duplicate this later
			//ProcessSettingsFromCategories(item.Categories, ref articleInfo);

			av.ModuleId = moduleInfo.ModuleId;
			av.ApprovalStatusId = (!item.Publish) ? 1 : 3;
			av.LastUpdated = DateTime.Now.ToString();
			av.AuthorUserId = userInfo.UserID;
			av.Thumbnail = featuredImage;

			// For style detection posts, we set the ArticleText to the content
			if (item.StyleDetectionPost) av.ArticleText = item.Content;

			av.Save(userInfo.UserID);

			articleId = av.ItemId;

			# region Code for Detecting the Site Template
			if (item.StyleDetectionPost)
			{
				// TODO change this to a URL in a format that shows the article entry.
				string tempInstallUrl = Globals.NavigateURL(moduleInfo.TabId, string.Empty, "itemid=" + av.ItemId);
				MetaPostServices.SetTempInstallUrl(ProviderKey, tempInstallUrl);
			}
			# endregion

			List<ItemCategory> itemCategories = new List<ItemCategory>();
			List<ItemCategoryInfo> categories = GetModuleCategories(portalSettings.PortalId);
			if (item.Categories.Length > 0)
			{
				// Create an ItemCategory array and send this to the SetItemCategories Method

				foreach (ItemCategoryInfo cat in categories)
				{
					foreach (string catInItem in item.Categories)
					{
						if (cat.CategoryName == catInItem)
						{
							ItemCategory ic = new ItemCategory();
							ic.CategoryId = cat.CategoryId.ToString();
							ic.CategoryName = cat.CategoryName;
							itemCategories.Add(ic);
							break;
						}
					}
				}
			}
			else
			{
				if (item.StyleDetectionPost)
				{
					// Set to first category in the list
					if (categories.Count > 0)
					{
						ItemCategory ic = new ItemCategory();
						ic.CategoryId = categories[0].CategoryId.ToString();
						ic.CategoryName = categories[0].CategoryName;
						itemCategories.Add(ic);
					}
				} // No longer needed since we check this above, but left for reference
				else
				{
					throw new MetaPostException("NoCategorySelected", "Please select a category.");
				}
			}

			SetItemCategories(av.ItemId.ToString(), itemCategories.ToArray(), userInfo, portalSettings);

			return articleId;
		}

		private List<ItemCategoryInfo> GetModuleCategories(int portalId)
		{
			// TODO - This isn't the best.  The best would be a list of categories for the specific module.
			// This list returns the categories for the entire portal.  Of course, maybe these are the same.
			DataTable catTable = Category.GetCategoriesHierarchy(portalId);
			List<ItemCategoryInfo> categoryList = new List<ItemCategoryInfo>();

			foreach (DataRow dr in catTable.Rows)
			{
				if ((int)dr["RelationshipTypeId"] == 28 || (int)dr["RelationshipTypeId"] == 29 || (int)dr["RelationshipTypeId"] == 36)
				{
					ItemCategoryInfo cat = new ItemCategoryInfo();
					cat.CategoryId = (int)dr["ItemId"];
					cat.CategoryName = dr["Name"].ToString();
					if (!dr.IsNull("ParentItemId")) cat.ParentId = (int)dr["ParentItemId"];
					categoryList.Add(cat);
				}
			}
			return categoryList;
		}

		# endregion

	}
}
